package com.alibaba.dingtalk.openapi.helper;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.dingtalk.openapi.vo.HttpGetEntity;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.constant.StringConstant;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.Objects;

/**
 * @author 吴峰
 * @date 2021/03/17 16:09
 */
@Component
public class RestTemplateHelper {

    private static final Logger LOG = LoggerFactory.getLogger(RestTemplateHelper.class);

    @Resource
    private RestTemplate restTemplate;


    /**
     * 发送post请求，response返回构造成JSONObject
     * @param url 请求地址
     * @param body 请求体参数
     * @return 请求响应封装成JSONObject
     */
    public JSONObject post(String url, JSONObject body) throws RestClientException {
        String ret = post(url, body, null, null);
        return JSON.parseObject(ret);
    }

    /**
     * 发送post请求
     * @param url 请求地址
     * @param body 请求参数
     * @param header 请求头
     * @param httpHeaders 请求封装类
     * @return java.lang.String
     * @author 吴峰
     * @date 2021/3/17 20:21
     */
    public String post(String url, JSONObject body, JSONObject header, HttpHeaders httpHeaders) {
        if (httpHeaders == null) {
            httpHeaders = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            httpHeaders.setContentType(type);
        }
        setHeader(header, httpHeaders);
        if (Objects.isNull(body)) {
            // 需给一个空json，避免解析body报错
            body = new JSONObject();
        }
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(body), httpHeaders);
        return restTemplate.postForObject(url, requestEntity, String.class);
    }

    /**
     * 发送put请求
     * @param url 请求地址
     * @param body 请求参数
     * @param header 请求头
     * @param httpHeaders 请求封装类
     * @return java.lang.String
     * @author 吴峰
     * @date 2021/3/17 20:21
     */
    public String put(String url, JSONObject body, JSONObject header, HttpHeaders httpHeaders) {
        if (httpHeaders == null) {
            httpHeaders = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            httpHeaders.setContentType(type);
        }
        setHeader(header, httpHeaders);
        if (Objects.isNull(body)) {
            // 需给一个空json，避免解析body报错
            body = new JSONObject();
        }
        HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(body), httpHeaders);
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
        return exchange.getBody();
    }

    /**
     * 发送get请求，response返回构造成JSONObject
     * @param url 请求地址
     * @param header 请求头参数
     * @return 请求响应封装成JSONObject
     */
    public JSONObject get(String url,JSONObject header) throws XbbException {
        String ret = get(url, null, header, null);
        return JSON.parseObject(ret);
    }
    /**
     * 发送get请求
     * @param url 请求地址
     * @param body 请求参数
     * @param header 请求头
     * @param httpHeaders 请求封装类
     * @return java.lang.String
     * @author 吴峰
     * @date 2021/3/17 20:21
     */
    public String get(String url, JSONObject body, JSONObject header, HttpHeaders httpHeaders) throws XbbException {

        try {
            if (httpHeaders == null) {
                httpHeaders = new HttpHeaders();
            }
            url = getUrlByParam(url, body);
            setHeader(header, httpHeaders);
            HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
            URL urlEntity = new URL(url);
            URI uri = new URI(urlEntity.getProtocol(), null, urlEntity.getHost(), urlEntity.getPort(), urlEntity.getPath(), urlEntity.getQuery(), null);
            ResponseEntity<String> responseEntity = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class);
            return responseEntity.getBody();
        } catch (Exception e) {
            LOG.error("RestTemplateHelp.get 获取失败：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }

    }

    /**
     * 设置请求头参数
     * @param header
     * @param httpHeaders
     */
    private void setHeader(JSONObject header, HttpHeaders httpHeaders) {
        if (header != null) {
            for (Map.Entry<String, Object> entry : header.entrySet()) {
                String k = entry.getKey();
                Object value = entry.getValue();
                if (value != null) {
                    httpHeaders.set(k, value.toString());
                }
            }
        }
    }

    /**
     * Description: 异步post，不返回结果
     * @param url
     * @param data
     * @return void
     * @author 魏荣杰
     * @date 2020/7/9 14:48
     */
    public static void asyncPost(final String url, JSONObject data, JSONObject header) {
        RequestConfig requestConfig = RequestConfig.custom().
                setSocketTimeout(5000).setConnectTimeout(5000).build();
        final CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build();

        httpAsyncClient.start();
        try {

            if (Objects.isNull(data)) {
               data = new JSONObject();
            }
            StringEntity httpEntity = new StringEntity(JSONObject.toJSONString(data), ContentType.APPLICATION_JSON);
            URL urlEntity = new URL(url);
            URI uri = new URI(urlEntity.getProtocol(), null, urlEntity.getHost(), urlEntity.getPort(), urlEntity.getPath(), urlEntity.getQuery(), null);

            final HttpPost httpPost = new HttpPost(uri);
            if (Objects.nonNull(header)) {
                header.forEach((key, value)->{
                    httpPost.addHeader(key, value.toString());
                });
            }
            httpPost.setEntity(httpEntity);
            httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
            httpAsyncClient.execute(httpPost,
                    new FutureCallback<HttpResponse>() {
                        @Override
                        public void completed(HttpResponse httpResponse) {
                            try {
                                LOG.error("request url, http code=" + httpResponse.getStatusLine().getStatusCode() + ", url=" + url);
                                if (httpResponse.getStatusLine().getStatusCode() != 200) {
                                    org.apache.http.HttpEntity entity = httpResponse.getEntity();
                                    if (entity != null) {
                                        String resultStr = EntityUtils.toString(entity, "utf-8");
                                        LOG.error("response error = " + resultStr);
                                    }
                                }

                            } catch (Exception e) {
                                LOG.error("request url=" + url + ", exception, msg=" + e.getMessage(), e);
                            } finally {
                                if (httpAsyncClient != null) {
                                    try {
                                        httpAsyncClient.close();
                                    } catch (IOException e) {
                                        LOG.error("POST execute httpAsyncClient close exception ", e, e);
                                    }
                                }
                            }

                        }

                        @Override
                        public void failed(Exception e) {
                            if (httpAsyncClient != null) {
                                try {
                                    httpAsyncClient.close();
                                } catch (IOException ex) {
                                    LOG.error("POST execute httpAsyncClient close exception ", ex, ex);
                                }
                            }
                        }

                        @Override
                        public void cancelled() {
                            if (httpAsyncClient != null) {
                                try {
                                    httpAsyncClient.close();
                                } catch (IOException e) {
                                    LOG.error("POST execute httpAsyncClient close exception ", e, e);
                                }
                            }
                        }
                    });

        } catch (Exception e) {
            LOG.error("request url=" + url + ", exception, msg=" + e.getMessage(), e);
        }

    }

    /**
     * get异步发送
     * @param url 链接
     * @param data 参数
     * @param header 请求头
     * @return void
     * @author 吴峰
     * @date 2021/3/19 17:18
     */
    public static void asyncGet(String url, JSONObject data, JSONObject header) {
        RequestConfig requestConfig = RequestConfig.custom().
                setSocketTimeout(5000).setConnectTimeout(5000).build();
        final CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build();

        httpAsyncClient.start();
        try {

            final String urlString = getUrlByParam(url, data);
            URL urlEntity = new URL(urlString);
            URI uri = new URI(urlEntity.getProtocol(), null, urlEntity.getHost(), urlEntity.getPort(), urlEntity.getPath(), urlEntity.getQuery(), null);
            final HttpGetEntity httpGet = new HttpGetEntity(uri);
            if (Objects.nonNull(header)) {
                header.forEach((key, value)->{
                    httpGet.addHeader(key, value.toString());
                });
            }
            httpGet.addHeader("Content-Type", "application/json;charset=UTF-8");
            httpAsyncClient.execute(httpGet,
                    new FutureCallback<HttpResponse>() {
                        @Override
                        public void completed(HttpResponse httpResponse) {
                            try {
                                LOG.error("GET request url, http code=" + httpResponse.getStatusLine().getStatusCode() + ", url=" + urlString);
                                if (httpResponse.getStatusLine().getStatusCode() != 200) {
                                    org.apache.http.HttpEntity entity = httpResponse.getEntity();
                                    if (entity != null) {
                                        String resultStr = EntityUtils.toString(entity, "utf-8");
                                        LOG.error("GET response error = " + resultStr);
                                    }
                                }

                            } catch (Exception e) {
                                LOG.error("request url=" + urlString + ", exception, msg=" + e.getMessage(), e);
                            } finally {
                                if (httpAsyncClient != null) {
                                    try {
                                        httpAsyncClient.close();
                                    } catch (IOException e) {
                                        LOG.error("GET execute httpAsyncClient close exception ", e, e);
                                    }
                                }
                            }

                        }

                        @Override
                        public void failed(Exception e) {
                            if (httpAsyncClient != null) {
                                try {
                                    httpAsyncClient.close();
                                } catch (IOException ex) {
                                    LOG.error("GET execute httpAsyncClient close exception ", ex, ex);
                                }
                            }
                        }

                        @Override
                        public void cancelled() {
                            if (httpAsyncClient != null) {
                                try {
                                    httpAsyncClient.close();
                                } catch (IOException e) {
                                    LOG.error("GET execute httpAsyncClient close exception ", e, e);
                                }
                            }
                        }
                    });

        } catch (Exception e) {
            LOG.error("request url=" + url + ", exception, msg=" + e.getMessage(), e);
        }

    }

    private static String getUrlByParam(String url, JSONObject body) {
        StringBuilder sb = new StringBuilder();
        sb.append(url);
        if (Objects.nonNull(body)) {
            if (!url.endsWith(StringConstant.QUESTION_MARK)) {
                sb.append(StringConstant.QUESTION_MARK);
            }
            body.forEach((k, value)->{
                if (sb.toString().endsWith(StringConstant.QUESTION_MARK)) {
                    sb.append(k).append("=").append(JSON.toJSONString(value));
                } else {
                    sb.append("&").append(k).append("=").append(JSON.toJSONString(value));
                }
            });
        }
        return sb.toString();
    }
}
